/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Implementation of class CSQLData
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 * $Id: SQLData.cpp,v 1.9 2009-08-31 12:58:15 amaula Exp $
 *
 */
#include "SQLData.hpp"
#include "owndebug.h"
#include "SQLResult.hpp"

//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
using namespace MaCI::SQL;
//*****************************************************************************
OWNDEBUG_INITIALIZE_MODULENUMBER(); // Initialize module print capability
//*****************************************************************************

CSQLData::CSQLData()
  : iTimestampPtr(NULL),
    iCommandPtr(NULL),
    iSQLQueryPtr(NULL),
    iSQLResultXMLPtr(NULL)
{
  SetLastElementType(KTypeUnknown);
}
//*****************************************************************************

CSQLData::~CSQLData()
{
  // Do any cleanups here.
}
//*****************************************************************************

CSQLData::CSQLData(const CSQLData &aData)
  :  iTimestampPtr(NULL),
     iCommandPtr(NULL),
     iSQLQueryPtr(NULL),
     iSQLResultXMLPtr(NULL)
{
  // Call assignment operator.
  *this = aData;
}
//*****************************************************************************

CSQLData &CSQLData::operator=(const CSQLData &aData)
{
  // Reset
  Reset();

  // Create an internal BinBag instance
  if (CreateInternalBinBag(aData.iBinBagContainer) == false) {
    dPrint(4,"WARNING: Failed to create internal binbag for object %p from object %p",
           this, &aData);
  }
  
  // Do the decode on the buffer; it sets pointers and ... stuff :)
  if (DecodeFrom(NULL) == false) {
    dPrint(4,"Warning: Failed to Decode CSQLData even though assignment operator used?");
  }
  
  return *this; 
}
//*****************************************************************************

bool CSQLData::DecodeFrom(gim::binbag::CBinBag *aBinBag)
{
  bool result = true;
  const gim::binbag::TBinBlob *b = NULL;
  int previous_type = -1;
  
  // Store pointer and test it for NullNess. only overwrite if the
  // argument was non-NULL.
  if (aBinBag != NULL) 
    SetBinBagContainerPtr(aBinBag);

  // Parse
  if (iBinBagContainer && iBinBagContainer->Rewind()) {
    while( (b = iBinBagContainer->GetNextBinary()) && result ) {
      switch(b->type) {

      case KTypeTimestamp:
	if (iTimestampPtr == NULL && 
            b->size == sizeof(Common::TTimestamp)) {
	  iTimestampPtr = reinterpret_cast<const Common::TTimestamp *>(b->data);

	} else result = false;
	break;

      case KTypeCommand: {
        if (iCommandPtr == NULL && 
            b->size == sizeof(TCommand)) {
          iCommandPtr = reinterpret_cast<const TCommand *>(b->data); 

        } else result = false;
        break;
      }

      case KTypeSQLResultXML: {
        if (iSQLResultXMLPtr == NULL && 
            b->size >= sizeof(TSQLResultXML)) {
          iSQLResultXMLPtr = reinterpret_cast<const TSQLResultXML *>(b->data); 
          
          // Check size validity (required on variable size fields)
          if (iSQLResultXMLPtr->length + sizeof(TSQLResultXML) != b->size) {
            iSQLResultXMLPtr = NULL;
            result = false;
          }
        } else result = false;
        break;
      }

      case KTypeSQLQuery: {
        if (iSQLQueryPtr == NULL && 
            b->size >= sizeof(TSQLQuery)) {
          iSQLQueryPtr = reinterpret_cast<const TSQLQuery *>(b->data); 
          
          // Check size validity (required on variable size fields)
          if (iSQLQueryPtr->length + sizeof(TSQLQuery) != b->size) {
            iSQLQueryPtr = NULL;
            result = false;
          }
        } else result = false;
        break;
      }

      default:
	// Unknown Type in BinBag. Do nothing.
	dPrint(10,"Unknown type %u, ignoring.", b->type);
        break;
      }
      
      // Store previous type
      previous_type = b->type;
    }
  } else result = false;

  // Store the result
  SetReadOnlyContainer(result);

  return result;
}
//*****************************************************************************

void CSQLData::Reset()
{
  // Call base class reset.
  CMaCIData::Reset();

  // Clean out. (Same values as in default constructor)
  iTimestampPtr = NULL;
  iCommandPtr = NULL;
  iSQLResultXMLPtr = NULL;
  iSQLQueryPtr = NULL;

  SetLastElementType(KTypeUnknown);
}
//*****************************************************************************

void CSQLData::Print(const int l) const
{
  const gim::binbag::TBinBlob *b = NULL;
  // Parse
  if (iBinBagContainer && iBinBagContainer->Rewind()) {
    while( (b = iBinBagContainer->GetNextBinary())) {
      switch(b->type) {
      case KTypeTimestamp: {
	const Common::TTimestamp *ts = 
          reinterpret_cast<const Common::TTimestamp *>(b->data);
	dPrint(l,"TTimestamp: s=%d, us=%d", 
	       ts->timestamp_s,
	       ts->timestamp_us);
	break;
      }
        
      case KTypeCommand: {
	const TCommand *ts = 
          reinterpret_cast<const TCommand *>(b->data);
	dPrint(l,"TCommand: cmd=%d, data=%d", 
	       ts->cmd, ts->data);
	break;
      }

      case KTypeSQLQuery: {
	const TSQLQuery *e = 
          reinterpret_cast<const TSQLQuery *>(b->data);
	dPrint(l,
               "TSQLQuery:\n"
               "** Begin **********************************************************************\n"
               "%s\n"
               "** End ************************************************************************", 
	       e->text);
        break;
      }

      case KTypeSQLResultXML: {
	const TSQLQuery *e = 
          reinterpret_cast<const TSQLQuery *>(b->data);
	dPrint(l,
               "TSQLResultXML:\n"
               "** Begin **********************************************************************\n"
               "%s\n"
               "** End ************************************************************************", 
	       e->text);
        break;
      }
	
      default:
	// Unknown Type in BinBag. Do nothing.
	dPrint(10,"Unknown type %u, ignoring.", b->type);
        break;
      }
    }
  }
}
//*****************************************************************************

bool CSQLData::SetTimestamp(const Common::TTimestamp &aStamp)
{
  bool result = false;
  if (!IsReadOnlyContainer() && iTimestampPtr == NULL) {
    ADDELEMENT(Common::TTimestamp, KTypeTimestamp, iTimestampPtr, aStamp);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CSQLData::SetCommand(const TCommand &aCmd)
{  
  bool result = false;
  if (!IsReadOnlyContainer() && iCommandPtr == NULL) {
    ADDELEMENT(TCommand, KTypeCommand, iCommandPtr, aCmd);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CSQLData::SetSQLResultXML(const TSQLResultXML &aXML)
{  
  bool result = false;
  if (!IsReadOnlyContainer() && iSQLResultXMLPtr == NULL) {

    // Insertion.
    const gim::binbag::TBinBlob *b = 
      iBinBagContainer->AddBinary(KTypeSQLResultXML, 
                                  (const char *)&aXML, 
                                  aXML.GetContainerSize());
    iSQLResultXMLPtr = reinterpret_cast<const TSQLResultXML *>(b->data);
    SetLastElementType(KTypeSQLResultXML);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CSQLData::SetSQLResultXMLString(const std::string &aXmlString)
{
  bool result = false;
  uint8_t *ptr = NULL;
  unsigned int bytes = 0;

  // Create dynamic container from xmlString
  TSQLResultXML *tcxml = TSQLResultXML::CreateTextElement(&ptr, bytes, aXmlString);
  
  // Use the normal setter function. (use result)
  result = SetSQLResultXML(*tcxml);

  // Unconditionally destroy dynamic element.
  TSQLResultXML::DestroyTextElement(tcxml);

  return result;
}
//*****************************************************************************

bool CSQLData::SetSQLQuery(const TSQLQuery &aQuery)
{
  bool result = false;
  if (!IsReadOnlyContainer() && iSQLQueryPtr == NULL) {

    // Insertion.
    const gim::binbag::TBinBlob *b = 
      iBinBagContainer->AddBinary(KTypeSQLQuery, 
                                  (const char *)&aQuery, 
                                  aQuery.GetContainerSize());
    iSQLQueryPtr = reinterpret_cast<const TSQLQuery *>(b->data);
    SetLastElementType(KTypeSQLQuery);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CSQLData::SetSQLQueryString(const std::string &aQueryString)
{
  bool result = false;
  uint8_t *ptr = NULL;
  unsigned int bytes = 0;

  // Create dynamic container from xmlString
  TSQLQuery *q = TSQLQuery::CreateTextElement(&ptr, bytes, aQueryString);
  
  // Use the normal setter function. (use result)
  result = SetSQLQuery(*q);

  // Unconditionally destroy dynamic element.
  TSQLQuery::DestroyTextElement(q);

  return result;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

const MaCI::Common::TTimestamp *CSQLData::GetTimestamp(void) const
{
  return iTimestampPtr;
}
//*****************************************************************************

const TCommand *CSQLData::GetCommand(void) const
{
  return iCommandPtr;
}
//*****************************************************************************

const TSQLQuery *CSQLData::GetSQLQuery(void) const
{
  return iSQLQueryPtr;
}
//*****************************************************************************

const TSQLResultXML *CSQLData::GetSQLResultXML(void) const
{
  return iSQLResultXMLPtr;
}
//*****************************************************************************

bool CSQLData::GetSQLResult(CSQLResult &aContainer) const
{
  bool result = false;
  if (iSQLResultXMLPtr) {
    // Got container.
    result = aContainer.DecodeSQLResultXML(iSQLResultXMLPtr->text);
  }
  return result;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

#ifdef COMPILE_SQLDATA_STANDALONE_MAIN
#include <string>

int main(void)
{
  using namespace MaCI::SQL;
  gim::binbag::CBinBag bb;
  CSQLData A;
  CSQLData B;
  bool r;

  debugInit();
  debugSetGlobalDebugLvl(5);

  printf("Carry out CSQLResult tests..\n");
  const std::string sqldata = 
    "<?xml version=\"1.0\"?>\n"
    "<sqlcontainer version=\"0.2\">\n"
    " <table cols=\"3\" rows=\"4\">\n"
    "  <r>\n"
    "   <f>0</f>\n"
    "   <f>amaula</f>\n"
    "   <f>1hailee</f>\n"
    "  </r>\n"
    "  <r>\n"
    "   <f>1</f>\n"
    "   <f>jari</f>\n"
    "   <f>2hailee</f>\n"
    "  </r>\n"
    "  <r>\n"
    "   <f>2</f>\n"
    "   <f>aiholtta</f>\n"
    "   <f>3hailee</f>\n"
    "  </r>\n"
    "  <r>\n"
    "   <f>3</f>\n"
    "   <f>morsko</f>\n"
    "   <f>4hailee</f>\n"
    "  </r>\n"
    " </table>\n"
    "</sqlcontainer>\n";

  CSQLResult sqlc;
  
  printf("Decoding example SQLResult XML string...");
  r = sqlc.DecodeSQLResultXML(sqldata);
  assert(r == true);
  printf("OK\n");
  printf("Walkthrough of decoded data...\n");
  // Get reference to table.
  const CSQLTable &t = sqlc.GetTable();
  const unsigned int cols = t.GetColumnCount();
  printf("table:\n");
  // Print contents.
  const unsigned int rows = t.GetRowCount();
  for(unsigned int r=0; r < rows; ++r) {
    printf("\t|");
    for(unsigned int c=0; c < cols; ++c) {
      const CSQLField &sqlf = t.GetField(r,c);
      printf("| %20s |", sqlf.GetValueString().c_str());
    }
    printf("|\n");
  }

  printf("Walktrough complete.\n");
  printf("Attempting to encode...");
  std::string another;
  r = sqlc.EncodeSQLResultXML(another);
  assert(r == true);

  printf("OK, original:\n");
  printf("****************************************************************************\n%s"
         "****************************************************************************\n", 
         sqldata.c_str());

  printf("OK, encoding result:\n");
  printf("****************************************************************************\n%s"
         "****************************************************************************\n", 
         another.c_str());

  // Compare!
  printf("Comparing original versus re-encoded version...");
  assert(another == sqldata);
  printf("OK (matches 1:1)\n");

  
  
  printf("CSQLResult tests completed OK.\n");


  printf("Carry out Encoding/Decoding test.\n");

  printf("Encoding...\n");
  // Do Encoding 
  A.EncodeTo(&bb);
  
  printf("Encode: Timestamp(1,2) ... ");
  r = A.SetTimestamp(MaCI::Common::TTimestamp(1, 2));
  assert(r == true);
  printf("OK.\n");

  const std::string testquery("select * from * where 1 = 1");
  printf("Encode: SQLQuery('%s') ... ", testquery.c_str());
  r = A.SetSQLQueryString(testquery);
  assert(r == true);
  printf("OK.\n");

  printf("Encode: SQLResultXML(...) ... ");
  r = A.SetSQLResultXMLString(another);
  assert(r == true);
  printf("OK.\n");

  printf("Encode: TCommand(KCommandExecuteSQLQuery) ... ");
  r = A.SetCommand(TCommand(KCommandExecuteSQLQuery, 666));
  assert(r == true);
  printf("OK.\n");
  
  /**
   * \todo Add test sections for TSQLEvent and TCommand
   */
  //////////////////////////////////////////////////////////
  printf("***********************************\n");
  printf("Decoding...\n");
  // Do Decoding
  B.DecodeFrom(&bb);
  printf("Decode: GetTimestamp() = (%d, %d) ... ", 
	 B.GetTimestamp()->timestamp_s, 
	 B.GetTimestamp()->timestamp_us);
  assert(B.GetTimestamp()->timestamp_s == 1);
  assert(B.GetTimestamp()->timestamp_us == 2);
  printf("Assertions OK.\n\n");

  ////////////////// GetSQLQuery()
  printf("Decode: GetSQLQuery() : \n");
  const TSQLQuery *q = B.GetSQLQuery();
  printf("Query string: '%s'\n", q->text); 
  assert(std::string(q->text) == testquery);
  printf("Assertions OK.\n\n");

  ////////////////// GetSQLResultXML()
  printf("Decode: GetSQLResultXML() : \n");
  const TSQLResultXML *x = B.GetSQLResultXML();
  printf("SQLResult XML:\n%s\n", x->text); 
  assert(std::string(x->text) == another);
  printf("Assertions OK.\n\n");

  ////////////////// GetCommand()
  printf("Decode: GetCommand() : \n");
  const TCommand *c = B.GetCommand();
  printf("Command: %d, data: %u (%08x)\n", c->cmd, c->data, c->data); 
  assert(c->cmd == KCommandExecuteSQLQuery);
  assert(c->data == 666);
  printf("Assertions OK.\n\n");
  


  printf("Test complete.\n");
  

}
#endif
//*****************************************************************************
//*****************************************************************************
